Loading...
 

Call structure in InstantView

Call structure in InstantView

InstantView is event-driven. This means that processing is always triggered by an event. In the simplest case, an event identifier is followed by a series of instructions which are processed one after the other. (See Receiving Events)

Example of simple processing
Button(ClickBtn, 0, 0, 80, T("Klick mich", "Click me")) [ SELECT: T("Der Knopf wurde gedrückt", "The button was clicked") Attention(, INFO) ]

Normally, however, an event requires a more complex response, so that it is necessary to structure the instructions. Two means are available in InstantView for this purpose: Procedures and messages.

Procedures

Procedures are primarily intended to structure source code, to combine meaningful blocks and to avoid duplication. They are defined with the keyword Define and can be called if they are in the same module or in a provider. Providers are modules that have been marked as providers in their external statement.

Messages

Messages are used for the loose coupling of modules. A module informs that something has happened. Who receives and processes this message (and if at all) is not known to the caller and can be controlled flexibly via external instructions. Exceptions are SendMsg(..., SUPER) and SendMsg(..., DIRECT). With SendMsg(, SUPER) the implementation of this message in the inherited module is called. With SendMsg(, DIRECT) the message is only sent to the object lying on the stack.

The Call Stack

When calling a procedure or a message via SendMsg, the processing of the caller is paused and continued after the called procedure or message has been completed.

Example

In the following example, when the event "SELECT" is triggered, the command GetValue, which is directly after "SELECT:", is first executed. The procedure GetObjectToProcess follows. Thus the 1 is placed on the stack next, then the comparison operations are executed etc. The assignment to the variable person is only done after the procedure GetObjectToProcess is finished.

Example of the control flow
Define(GetObjectToProcess) 1 = if CreateTransObject(CX_PERSON) else CreatePersObject(CX_PERSON) ; [...] Integer(objectType, 0, 0, 20) [ Var(person) SELECT: GetValue GetObjectToProcess -> person ]

The position in the processing of the SELECT message is noted and placed virtually in the so-called frame on the call stack. The frame of the procedure GetObjectToProcess follows on top of this stack. When this is completed, its frame is removed and the execution of the frame of the SELECT message that is now at the top is continued.

Schematic representation of the call stack in this example
Schematic representation of the call stack in this example

The above example looks like this in the VSCode debugger:

Representation of the call stack in the VSCode debugger
Representation of the call stack in the VSCode debugger

Since these commands are thus in chronological order, they are called synchronous calls. Since the caller does not continue to calculate, this is also referred to as a "blocking call".

The Event Queue

In some cases, however, it is necessary to execute a sequence of commands after the current execution has been completed. PostMsg can be used for this purpose.

The current stack content is compiled together with the recipient and the event to be triggered and is processed as soon as the current call stack is processed.

The call stack always has a so-called root event (in the above case the SELECT event) and these root events are processed one after the other. We call this structure the event queue, because the first event that is placed in the queue is processed first. The event of PostMsg is now placed at the end of the event queue - just like all other events that occur during the processing of the first root event - and is processed after all previous root events have been processed.

Schematic representation of the relationship between call stack and event queue
Schematic representation of the relationship between call stack and event queue

Transactions

The implicit transaction handling in ClassiX opens a transaction at the first access to persistent data and ends it when the last root event from the event queue has been processed. Thus, all data manipulations belonging to one trigger are combined in one transaction.

Independently of this, transactions can be started with BeginTXN, completed with EndTXN and aborted with AbortTXN.

return, cancel and exceptions

With return the processing of the current frame can be terminated. The control flow is then continued in the next frame on the call stack.

With cancel, on the other hand, the processing of the entire call stack and the event queue can be terminated. This means in particular that PostMsg calls are no longer processed.

Exceptions generally behave like a cancel: The call stack is cleared and if the exception did not occur within an ExecuteWeak/CallWeak, then the transaction is subsequently cancelled, the event queue is emptied and the user is shown an error message indicating what has happened.

If exceptions occur within a call of a class method, they can be intercepted by CallWeak and then read out by SystemObject::GetLastError.

Special cases

The structure described so far is interrupted in a few cases.

WaitOnInput

WaitOnInput stops processing in the current state and continues processing on an empty1 copy of the event queue and call stack. Processing continues on this copy until FinishInput ends this state. This means that processing can be continued in the same frame at the same point after WaitOnInput .

1= The event queue is an empty copy, but root events such as UI events and PostMsg are placed in a separate, central queue for root events, from which these events are copied out if the current processing is completed. This results in a PostMsg being processed before WaitOnInput within WaitOnInput.

Note: A call to cancel and exceptions within WaitOnInput terminate both the inner call stack and that of the calling WaitOnInput. Execution will therefore not continue after a cancel after the WaitOnInput .

Exception: Due to the special processing method of code in the ClassiX shell, an exception within the ClassiX shell within WaitOnInput currently leads to the exception being reported and the code being continued after the WaitOnInput .

Attention: Messages placed in the event queue via PostMsg are not "frozen" in the event queue outside WaitOnInput, but are processed in the event queue within WaitOnInput . Since the event queue is free at this moment, the messages are even executed immediately.

Attention: The following constellation currently represents an exception to the above rule:
PostMsg(WAIT) PostMsg(A) PostMsg(B)
If the message WAIT executes a WaitOnInput, then the PostMsg requests for WAIT, A & B have already been copied into the local event queue and remain frozen there until the WaitOnInput command has been completed.

If FinishInput is called, the remaining commands of the "inner" call stack are processed. Then the execution returns to the command behind WaitOnInput .

Before Dll version 225137:
PostMsg calls within the call stack of FinishInput are lost except for a maximum of one. The PostMsg call that is not lost is executed after processing the inner call stack and before the command after WaitOnInput .

Runtime Commands

In general, a cancel or an exception causes the processing to be aborted and an attempt to return to a safe state. However, if the call stack is not so simply structured that only procedure calls and SendMsg, but also runtime commands are involved, the rules are more differentiated.

OpenWindow

While processing the OpenWindow command, the INITIALIZE event, among other things, is sent to the corresponding window. If an exception occurs here or if a cancel is executed, the opening of the window is undone, the transaction is cancelled and the event queue is emptied.

CloseWindow

While processing the command CloseWindow the events CLOSE and NON_CURRENT are sent to the corresponding window for information. If an exception occurs here or a cancel is executed, closing the window cannot be reversed to a consistent state.

Exceptions in this critical state will therefore cause the machine to stop.

Calls to cancel only stop processing the CLOSE event (as if it were a root event), but do not empty the event queue, so a PostMsg will still be executed afterwards. The window will be closed despite cancel , because closing in CLOSE can no longer be aborted.

If the user closes a window, the system event CLOSING is sent before the window is actually closed. In this case, exceptions and cancel calls within this message do not close the window.

Access pressures in ObjectBox

In ObjectBoxes, access expressions can be specified via SetFormat, which are used to fill the cells. If an exception occurs here, the process of filling is not aborted. Instead, the error is displayed in red in the corresponding cell.